<?php
namespace app;

use app\common\exception\BaseException;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\HttpResponseException;
use think\exception\ValidateException;
use think\facade\Env;
use think\facade\Log;
use think\Response;
use Throwable;

/**
 * 应用异常处理类
 */
class ExceptionHandle extends Handle
{
    private $code;
    private $msg;
    private $errorCode;

    /**
     * 不需要记录信息（日志）的异常类列表
     * @var array
     */
    protected $ignoreReport = [
        HttpException::class,
        HttpResponseException::class,
        ModelNotFoundException::class,
        DataNotFoundException::class,
        ValidateException::class,
    ];

    /**
     * 记录异常信息（包括日志或者其它方式记录）
     *
     * @access public
     * @param  Throwable $exception
     * @return void
     */
    public function report(Throwable $exception): void
    {
        if ($exception instanceof BaseException) {
            // 如果是自定义异常
            $this->code = $exception->code;
            $this->msg = $exception->msg;
            $this->errorCode = $exception->errorCode;

            // 自定义记录日志的格式
            $errorRecord = [
                'referer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '',
                'msg' => $exception->msg,
                'errorCode' => $exception->errorCode
            ];

            Log::record(json_encode($errorRecord, JSON_UNESCAPED_UNICODE), 'error');
        } else {
            if (!$this->app->isDebug()) {
                $this->code = 500;
                $this->msg = '服务器内部错误，不想告诉你';
                $this->errorCode = 999;
                $this->recordErrorLog($exception);
            }
        }

        // 使用内置的方式记录异常日志
        parent::report($exception);
    }

    /**
     * Render an exception into an HTTP response.
     *
     * @access public
     * @param \think\Request   $request
     * @param Throwable $e
     * @return Response
     */
    public function render($request, Throwable $e): Response
    {
        // 如果开启了调试模式并且不是使用自定的异常处理函数
        // 判断是否为不需要处理的类
        $useHandle = false;
        foreach ($this->ignoreReport as $item) {
            if ($e instanceof $item) {
                $useHandle = true;
                break;
            }
        }
        if ((!($e instanceof BaseException) && Env::get('app_debug', false)) || $useHandle) {
            return parent::render($request, $e);
        }

        $result = [
            'msg' => $this->msg,
            'errorCode' => $this->errorCode,
            'request_url' => $request->url(),
        ];

        return Response::create($result,'json',$this->code);
    }

    private function recordErrorLog(\Exception $e)
    {
        $errorMsg = $e->getFile() . ':' . $e->getLine() . ' ' . $e->getMessage() . '(' . $e->getCode() . ')' . get_class($e);
        Log::record($errorMsg, 'error');
    }
}
